package cn.jeasy.core.bean.factory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Resource;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.jeasy.core.bean.BeanDefinition;
import cn.jeasy.core.bean.BeanPostProcessor;
import cn.jeasy.core.bean.BeanReference;
import cn.jeasy.core.bean.PropertyValue;
import cn.jeasy.core.util.CollectionUtil;
import cn.jeasy.core.util.StringUtil;


/**
 * BeanFactory 的一种抽象类实现，规范了 IoC 容器的基本结构。
 * IoC 容器的结构：AbstractBeanFactory 维护一个 beanDefinitionMap 哈希表用于保存类的定义信息（BeanDefinition）。
 *
 */
public class DefaultBeanFactory implements BeanFactory{
	  private static final Logger logger = LoggerFactory.getLogger(DefaultBeanFactory.class);
	/**
	 * bean定义的信息和bean的name保存在线程安全的HashMap中
	 */
	private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(20);
	
	/**
	 * 保存完成注册的bean的name
	 */
	private final List<String> beanDefinitionNames = new ArrayList<String>(20);
	
	/**
	 * 增加bean处理程序：
	 * 例如通过AspectJAwareAdvisorAutoProxyCreator#postProcessAfterInitialization()实现AOP的织入
	 */
	private List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>(5);
	
	private static  DefaultBeanFactory instance=new DefaultBeanFactory();
	
	private DefaultBeanFactory(){}
	
	public synchronized static DefaultBeanFactory getInstance(){
		return instance;
	}
	
	
	/**
	 * 是否存在bean
	 */
	@Override
	public boolean contains(String name){		
		 return beanDefinitionNames.contains(name);
	}
	/**
	 * 根据名字获取bean实例(实例化并初始化bean)
	 * @throws Exception 
	 */
	@Override
	public Object getBean(String name) throws Exception {
		// 获取该bean的定义
		BeanDefinition beanDefinition = beanDefinitionMap.get(name);
		// 如果没有这个bean的定义就抛异常
		if (beanDefinition == null){
			logger.warn("not found bean named {}",name);
			return null;
		}
		Object bean = beanDefinition.getBean();
		// 如果还没有装配bean
		if (bean == null) {
			// 装配bean（实例化并注入属性）
			return doCreateBean(name,beanDefinition);
		}
		return bean;
	}

	/**
	 * 装载bean
	 * @param beanDefinition
	 * @return
	 * @throws Exception
	 */
	protected Object doCreateBean(String name,BeanDefinition beanDefinition) throws Exception {
		// 实例化bean
		Object bean = createBeanInstance(beanDefinition);
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
			 beanPostProcessor.postProcessBeforeInitialization(bean, name);
		}
		
		beanDefinition.setBean(bean);
		// 注入属性的hook方法(参考模板方法设计模式中的hook方法)交给子类去实现
		// 例如：AutowireCapableBeanFactory.java实现了自动装配
		applyPropertyValues(bean, beanDefinition);
		// 初始化bean后
		// 例如：生成相关代理类,用于实现AOP织入
		// 可以看看AutowireCapableBeanFactory的postProcessAfterInitialization()方法实现
		// 返回的可能是代理对象
		for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
             beanPostProcessor.postProcessAfterInitialization(bean, name);
		}
	   return bean;
	}
	/**
	 * 创建对象实例
	 * @param beanDefinition
	 * @return
	 * @throws Exception
	 */
	protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
		return beanDefinition.getBeanClass().newInstance();
	}
	/**
	 * 通过set方法给属性实例化
	 * 	/**
	 * 通过反射自动装配bean的所有属性
     *
	 * 例如：<bean id="dog" class="com.ysj.entity.Dog">
	 *					<property name="Id" value="101"></property>
	 *				</bean>
	 *			先获取dog对象的setId方法，然后通过反射调用该方法将value设置进去
	 *			getDeclaredMethod方法的第一个参数是方法名，第二个参数是该方法的参数列表
	 *
	 * @param bean
	 * @param beanDefinition
	 * @throws Exception
	 */
	private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
		if (bean instanceof BeanFactoryAware) {
			((BeanFactoryAware) bean).setBeanFactory(this);
		}
		// xml配置方式属性注入
		for (PropertyValue propertyValue : beanDefinition.getPropertyValues()) {
			Object value = propertyValue.getValue();
			// 如果属性是ref而不是value类型就先实例化那个ref的bean，然后装载到这个value里
			if (value instanceof BeanReference) {
				BeanReference beanReference = (BeanReference) value;
				// 先实例化ref的bean再装配进去
				value = getBean(beanReference.getName());
			}
			
			try{
				// 拼接方法名
				Method declaredMethod = bean.getClass().getDeclaredMethod(
						"set" +StringUtil.firstToUpper(propertyValue.getName()), 
						value.getClass());
				declaredMethod.setAccessible(true);
				declaredMethod.invoke(bean, value);
			} catch (NoSuchMethodException e) {
				// 如果该bean没有setXXX的类似方法，就直接将value设置到相应的属性域内
				Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
				declaredField.setAccessible(true);
				declaredField.set(bean, value);
			}
		}
		// 注解方式  resoucest和inject 
		autowiredPropertyValues(bean);
	}
	/**
	 * 注解方式给属性实例化
	 * @throws Exception 
	 */
	public void autowiredPropertyValues(Object bean) throws Exception{
		 Field[] fields = bean.getClass().getDeclaredFields();
		 for(Field f:fields){
			 if(f.isAnnotationPresent(Resource.class)||f.isAnnotationPresent(Inject.class)){
				 //按名称注入
				 Resource r=f.getAnnotation(Resource.class);
				 if(r!=null){					
					 String beanName=r.name();
					 logger.debug("{} autowiredPropertyValues",beanName);
					 if(StringUtil.isNullOrEmpty(beanName)){
						 beanName=StringUtil.firstToLower(f.getType().getSimpleName());
					 }
					 if(contains(beanName)){//bean name存在
						  f.setAccessible(true); // 将字段设置为 public
				          f.set(bean, getBean(beanName)); // 设置字段初始值
				          continue;
					 }
				 }
				 injectPropertyValuesByType(bean, f);// 按类型
			 }
		 }
	}
	/**
	 * 按类型注入
	 * @param bean
	 * @param f
	 * @throws Exception 
	 */
	private void injectPropertyValuesByType(Object bean, Field f ) throws Exception{
		List<Object> implementsClass=getBeansForType(f.getType());
		if(CollectionUtil.isEmpty(implementsClass)){
			throw new NoClassDefFoundError(f.getType().getName()+ " is not found");
		}
		Object value=implementsClass.get(0);
		 f.setAccessible(true); // 将字段设置为 public
         f.set(bean, value); // 设置字段初始值
	}

	/**
	 * 预处理bean的定义，将bean的名字提前存好,实现Ioc容器中存储单例bean
	 * @throws Exception
	 */
	public void preInstantiateSingletons() throws Exception {
		Iterator<String> it = beanDefinitionNames.iterator();
		while (it.hasNext()) {
			String beanName = it.next();
			getBean(beanName);
		}
	}
	
	/**
	 * 根据类型获取所有bean实例
	 * @param type
	 * @return
	 * @throws Exception
	 */
	public List<Object> getBeansForType(Class<?> type) throws Exception{
		List<Object> beans = new ArrayList<Object>();
		for (String beanDefinitionName : beanDefinitionNames) {
			/**
			 * boolean isAssignableFrom(Class<?> cls)  
			 * 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口。
			 */
			if (type.isAssignableFrom(beanDefinitionMap.get(beanDefinitionName).getBeanClass())) {
				beans.add(getBean(beanDefinitionName));
			}
		}
		return beans;
	}
	/**
	 * 注册bean到容器
	 * @param name
	 * @param beanDefinition
	 * @throws Exception
	 */
	public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
		beanDefinitionMap.put(name, beanDefinition);
		beanDefinitionNames.add(name);
	}
	
	/**
	 * 增加bean处理程序，例如AspectJAwareAdvisorAutoProxyCreator#postProcessAfterInitialization()
	 * @param beanPostProcessor
	 * @throws Exception
	 */
	public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) throws Exception {
		beanPostProcessors.add(beanPostProcessor);
	}
	
	
}
